package com.gitee.huanminabc.utils_tools.redis.redisson.config;

/**
 * 简要描述
 * https://blog.csdn.net/idto315/article/details/133141062
 * @Author: huanmin
 * @Date: 2022/8/1 17:37
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */

import com.gitee.huanminabc.utils_tools.redis.config.RedisAutoConfiguration;
import org.apache.logging.log4j.util.Strings;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.HostAndPort;

import java.util.*;
import java.util.stream.Collectors;

@Configuration
public class RedissonConfig  implements InitializingBean {
    private static String ADDRESS_PREFIX = "redis://";
    public static  Map<Integer,RedissonClient> redissonClientMap = new HashMap<>();

    @Autowired
    private RedisAutoConfiguration redisAutoConfiguration;

    public Config singleRedissonServerConfig(RedisAutoConfiguration redisAutoConfiguration) {
        RedisAutoConfiguration.PoolRedisConfig pool = redisAutoConfiguration.getPool();
        String singleNode = redisAutoConfiguration.getSingleNode();
        RedisAutoConfiguration.NodeRedisConfig nodeConfig = redisAutoConfiguration.getNodeConfig(singleNode);
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer();
        if (Strings.isNotBlank(nodeConfig.getPassword())) {
            singleServerConfig.setPassword(nodeConfig.getPassword());
        }
        singleServerConfig.setAddress("redis://" + nodeConfig.getHost() + ":" + nodeConfig.getPort());
        singleServerConfig.setKeepAlive(true);

        singleServerConfig.setTimeout(pool.getTimeout());//Redis服务器响应超时。Redis命令成功发送后开始倒计时。
        singleServerConfig.setConnectTimeout(pool.getTimeout());//连接超时
        singleServerConfig.setPingConnectionInterval(pool.getTimeout());//ping连接间隔
        singleServerConfig.setConnectionMinimumIdleSize(pool.getMinIdle()); //最小空闲Redis连接量
        singleServerConfig.setConnectionPoolSize(pool.getMaxIdle()); //Redis连接池大小
        singleServerConfig.setDatabase(redisAutoConfiguration.getDbIndex());
        return config;
    }

    private Config sentinelRedissonServerConfig(RedisAutoConfiguration redisAutoConfiguration) {
        RedisAutoConfiguration.PoolRedisConfig pool = redisAutoConfiguration.getPool();
        String masterName = redisAutoConfiguration.getSentinelMaster();
        // 声明一个配置类
        Config config = new Config();
        SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
        sentinelServersConfig.setMasterName(masterName);
        //扫描间隔
        sentinelServersConfig.setScanInterval(2000);
        sentinelServersConfig.setTimeout(pool.getTimeout());

        sentinelServersConfig.setPingConnectionInterval(pool.getTimeout());
        sentinelServersConfig.setConnectTimeout(pool.getTimeout());

        sentinelServersConfig.setMasterConnectionPoolSize(pool.getMaxIdle());
        sentinelServersConfig.setSlaveConnectionPoolSize(pool.getMaxIdle());

        sentinelServersConfig.setMasterConnectionMinimumIdleSize(pool.getMinIdle());
        sentinelServersConfig.setSlaveConnectionMinimumIdleSize(pool.getMinIdle());

        sentinelServersConfig.setDatabase(redisAutoConfiguration.getDbIndex());

        List<Map<String, RedisAutoConfiguration.NodeRedisConfig>> nodes = redisAutoConfiguration.getNodes();
        String password = ""; //哨兵模式中redis的密码是一样的
        Set<HostAndPort> sentinels = new HashSet<>();
        for (Map<String, RedisAutoConfiguration.NodeRedisConfig> node : nodes) {
            for (Map.Entry<String, RedisAutoConfiguration.NodeRedisConfig> entry : node.entrySet()) {
                RedisAutoConfiguration.NodeRedisConfig nodeConfig = entry.getValue();
                sentinels.add(new HostAndPort(nodeConfig.getHost(), nodeConfig.getPort()));
                if ("".equals(password)) {
                    password = nodeConfig.getPassword();
                } else {
                    if (!password.equals(nodeConfig.getPassword())) {
                        throw new RuntimeException("哨兵模式中redis的密码不一致");
                    }
                }

            }
        }

        if (Strings.isNotBlank(password)) {
            sentinelServersConfig.setPassword(password);
        }
        //因为底层会以:分割,所以这里需要转换为Set
        Set<String> nodesStr = sentinels.stream().map((data) -> ADDRESS_PREFIX + data.getHost() + ":" + data.getPort()).collect(Collectors.toSet());
        // 添加redis节点
        sentinelServersConfig.addSentinelAddress(nodesStr.toArray(new String[nodesStr.size()]));

        return config;
    }

    public Config clusterRedissonServerConfig(RedisAutoConfiguration redisAutoConfiguration) {
        RedisAutoConfiguration.PoolRedisConfig pool = redisAutoConfiguration.getPool();
        // 声明一个配置类
        Config config = new Config();
        ClusterServersConfig clusterServersConfig = config.useClusterServers();
        clusterServersConfig.setScanInterval(2000);
        clusterServersConfig.setTimeout(pool.getTimeout());
        clusterServersConfig.setPingConnectionInterval(pool.getTimeout());
        clusterServersConfig.setConnectTimeout(pool.getTimeout());

        clusterServersConfig.setMasterConnectionPoolSize(pool.getMaxIdle());
        clusterServersConfig.setSlaveConnectionPoolSize(pool.getMaxIdle());
        clusterServersConfig.setMasterConnectionMinimumIdleSize(pool.getMinIdle());
        clusterServersConfig.setSlaveConnectionMinimumIdleSize(pool.getMinIdle());

        List<Map<String, RedisAutoConfiguration.NodeRedisConfig>> nodes = redisAutoConfiguration.getNodes();
        String password = ""; //哨兵模式中redis的密码是一样的
        Set<HostAndPort> sentinels = new HashSet<>();
        for (Map<String, RedisAutoConfiguration.NodeRedisConfig> node : nodes) {
            for (Map.Entry<String, RedisAutoConfiguration.NodeRedisConfig> entry : node.entrySet()) {
                RedisAutoConfiguration.NodeRedisConfig nodeConfig = entry.getValue();
                sentinels.add(new HostAndPort(nodeConfig.getHost(), nodeConfig.getPort()));
                if ("".equals(password)) {
                    password = nodeConfig.getPassword();
                } else {
                    if (!password.equals(nodeConfig.getPassword())) {
                        throw new RuntimeException("哨兵模式中redis的密码不一致");
                    }
                }

            }
        }
        if (Strings.isNotBlank(password)) {
            clusterServersConfig.setPassword(password);
        }
        //因为底层会以:分割,所以这里需要转换为Set
        Set<String> nodesStr = sentinels.stream().map((data) -> ADDRESS_PREFIX + data.getHost() + ":" + data.getPort()).collect(Collectors.toSet());
        clusterServersConfig.addNodeAddress(nodesStr.toArray(new String[nodesStr.size()]));
        return config;
    }



    public Config getConfig() {
        String model = redisAutoConfiguration.getModel();
        if ("single".equals(model)) {
            return singleRedissonServerConfig(redisAutoConfiguration);
        }
        if ("sentinel".equals(model)) {
            return sentinelRedissonServerConfig(redisAutoConfiguration);
        }
        if ("cluster".equals(model)) {
            return clusterRedissonServerConfig(redisAutoConfiguration);
        }
        throw new RuntimeException("不支持的redis模式,只支持single,sentinel,cluster模式");
    }


    @Bean
    public RedissonClient getRedisson() {
        return Redisson.create(getConfig());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (!redisAutoConfiguration.isSwitchDb()){
            redissonClientMap.put(redisAutoConfiguration.getDbIndex(),Redisson.create(getConfig()));
            return;
        }
        Integer dbIndex = redisAutoConfiguration.getDbIndex();//保留原来的数据库
        for (int i = 0; i <= 15; i++) {
            redisAutoConfiguration.setDbIndex(i);
            RedissonClient redissonClient = Redisson.create(getConfig());
            redissonClientMap.put(i,redissonClient);//创建16个数据库的redissonClient
        }
        redisAutoConfiguration.setDbIndex(dbIndex);//还原原来的数据库
    }
}


